#include "string.h"
#include "stdlib.h"
#include "stdio.h"
//USER API
#include "tuya_user_api.h"
//OS API
#include "tal_mutex.h"
#include "tal_semaphore.h"
#include "tal_queue.h"
#include "tal_thread.h"
#include "tal_sw_timer.h"
#include "tal_gpio.h"
#include "tal_uart.h"
#include "tkl_adc.h"
#include "iot_board_sensor.h"
#include "ssd1306.h"

/***********************************************************
*************************variable define********************
***********************************************************/
// fot tuya_set_product_key
#define PRODUCT_KEY        "6czvurcyhs0l0ovm"
// Demo define dp
#define  DPID_temp_current				1
#define  DPID_humidity_value	        2
#define  DPID_battery_percentage        4
#define  DPID_maxtemp_set               10
#define  DPID_maxhum_set                12

#define  SAMPLE_PACKET_SEND_SUM     "ty_sample_send_sum"
#define  SAMPLE_PACKET_SEND_SUCCED  "ty_sample_send_succed"
static  int   packet_send_sum = 0;
static  int   packet_send_succeed = 0;
static  int   on_time = 0;

static THREAD_HANDLE    ty_test_thread = NULL;
static QUEUE_HANDLE     ty_test_queue = NULL;
static MUTEX_HANDLE     ty_test_mutex = NULL;   //mutex lock
static SEM_HANDLE       ty_test_sem = NULL;     //semaphore
static TIMER_PARAM_S    ty_test_timer;



typedef struct
{
    uint8_t dpid;
    uint8_t type;
    uint16_t len;
} com_dp_data_t;
/***********************************************************
*************************function define********************
***********************************************************/

/***********************************************************
************************* function *************************
***********************************************************/
int pre_init(void)
{
    /*
    该位置仅可以添加系统启动时硬件相关的初始化操作例如GPIO、ADC、I2C等
    注：UART1不可在此处配置，请在device_init()内配置
        请不要在此处操作长时间延时
    */

    ssd1306_power(true);
    tuya_i2c_init();
    OLED_Init();
    iot_board_sensor_ui_create();
    return 0;
}


int tuya_user_prod_test(USHORT_T cmd, UCHAR_T *data, UINT_T len, OUT UCHAR_T *ret_data, OUT USHORT_T *ret_len)
{
    return 0;
}

static int tuya_sample_kv_read(IN char* str, OUT int *data)
{
    int ret = OPRT_COM_ERROR;
    int len = sizeof(int);
    if(NULL == data){
        USER_API_LOGE("null err");
        return ret;
    }
    ret = tuya_user_api_config_item_get(str, data, &len);
    if(0 != ret){
        USER_API_LOGD("tuya_user_api_config_item_get none:%d", ret);
    }
    return ret;
}

static void tuya_sample_kv_write(IN char* str,IN int data)
{
    int ret;
    int len = sizeof(int);
    int baudrate_tmp;

    ret = tuya_user_api_config_item_get(str, &baudrate_tmp, &len);
    if(0 != ret){
        USER_API_LOGD("tuya_user_api_config_item_get none:%d", ret);
    }

    if(baudrate_tmp == data){
        return;
    }

    ret = tuya_user_api_config_item_set(str, &data, sizeof(int));
    if(0 != ret){
        USER_API_LOGE("tuya_user_api_config_item_set fail:%d", ret);
        return;
    }
}



static uint8_t tuya_dp_write_cb(IN lwdp_object_t* dataP)
{
    uint16_t len;
    size_t str_len;
    int32_t value;

    if(NULL == dataP){
        USER_API_LOGE("dataP NULL");
        return CODE_UNKOWN_ERROR;
    }

    while(NULL != dataP){
        com_dp_data_t* dp_data = NULL;
        uint8_t* send_buffer = NULL;
        uint8_t* p_dp_data_buf = NULL;

        len = tuya_user_api_get_lwdp_object_length(dataP);
        send_buffer = (uint8_t*)Malloc(sizeof(com_dp_data_t) + len + 1);
        if (send_buffer == NULL) {
            USER_API_LOGE("send_buffer is NULL");
            return CODE_UNKOWN_ERROR;
        }

        memset(send_buffer, 0, sizeof(com_dp_data_t) + len + 1);
        dp_data = (com_dp_data_t*)send_buffer;
        p_dp_data_buf = (uint8_t *)dp_data + sizeof(com_dp_data_t);

        switch (dataP->type) {
        case DP_TYPE_BOOLEAN:
            tuya_user_api_lwdp_decode_bool(dataP, (bool*)p_dp_data_buf);
            break;

        case DP_TYPE_INTEGER:{
            tuya_user_api_lwdp_decode_int(dataP, &value);
            if(dataP->id == DPID_maxtemp_set || dataP->id == DPID_maxhum_set){
                //下发的配置信息 自动上报ACK 也可自行处理

                uint8_t i = 0;
                lwdp_object_t* dataP_new = tuya_user_api_lwdp_object_new(1);

                dataP_new[i].id = dataP->id;
                tuya_user_api_lwdp_encode_int(value, &dataP_new[i++]);

                tuya_user_api_dp_report(true, i, dataP_new);
                tuya_user_api_lwdp_object_free(1, dataP_new);
            }

            }
            break;

        case DP_TYPE_RAW:
            tuya_user_api_lwdp_decode_raw(dataP, p_dp_data_buf, &str_len);
            break;

        case DP_TYPE_ENUM:
            tuya_user_api_lwdp_decode_enum(dataP, p_dp_data_buf);
            break;

        case DP_TYPE_STRING:
            tuya_user_api_lwdp_decode_string(dataP, (char*)p_dp_data_buf, &str_len);
            break;
        }
        dp_data->dpid = dataP->id;
        dp_data->type = dataP->type;
        USER_API_LOGI("tuya_dp_write_cb DP:%d, type:%d, len:%d", dp_data->dpid, dp_data->type, len);

        dp_data->len = WORD_SWAP(len);

        // if(DPID_HEARTBEAT_INTERVAL == dp_data->dpid){
        //     uint32_t lifetime = 0;
   		// 	if (len == 4) {
		//         ((uint8_t*)(&lifetime))[0] = p_dp_data_buf[3];
		//         ((uint8_t*)(&lifetime))[1] = p_dp_data_buf[2];
		//         ((uint8_t*)(&lifetime))[2] = p_dp_data_buf[1];
		//         ((uint8_t*)(&lifetime))[3] = p_dp_data_buf[0];
	    //     	tuya_user_api_lifetime_set(lifetime);
	    //     }	
        // }
        
        //TODO:这里开始可以处理下发的数据包dp_data.....

        Free(send_buffer);
        dataP = dataP->next;
    }

    return CODE_NO_ERROR;
}


static void dev_state_report(void)
{
    USER_API_LOGD("###### dev_state_report ######");
#define OBJ_SIZE 6
    uint8_t i = 0;
    lwdp_object_t* dataP = tuya_user_api_lwdp_object_new(OBJ_SIZE);

    if (dataP == NULL) {
        USER_API_LOGE("dataP == NULL");
        return;
    }

    iot_board_sensor sensor_report;
    get_sensor_value(&sensor_report);

    USER_API_LOGI("---------------------->>> %d %d",(int)sensor_report.temp_current*10, (int)sensor_report.humidity_value);

    dataP[i].id = DPID_temp_current;
    tuya_user_api_lwdp_encode_int((int)(sensor_report.temp_current*10), &dataP[i++]);

    dataP[i].id = DPID_humidity_value;
    tuya_user_api_lwdp_encode_int((int)sensor_report.humidity_value, &dataP[i++]);

    dataP[i].id = DPID_battery_percentage;
    tuya_user_api_lwdp_encode_int((int)iot_board_sensor_get_vbat(), &dataP[i++]);

    tuya_user_api_dp_report(true, i, dataP);
    tuya_user_api_lwdp_object_free(OBJ_SIZE, dataP);
#undef OBJ_SIZE   
}

static void dp_report_notify_callback(uint16_t msgid, uint8_t result)
{
    USER_API_LOGI("dp_report_notify_callback id: %d, result:%d", msgid, result);
    if(CODE_NO_ERROR == result){
        packet_send_succeed++;
        tuya_sample_kv_write(SAMPLE_PACKET_SEND_SUCCED, packet_send_succeed);
    }
}

static OPERATE_RET tuya_event_process_cb(void* ctx, system_event_t* event)
{
    USER_API_LOGD("tuya user event:%d",(event->event_id));
    iot_board_sensor_display(event->event_id, NULL, 0);
    switch (event->event_id) {

    case SYSTEM_EVENT_NETWORK_READY:
        break;

    case EVENT_LWM2M_CONNECTED:
        
        break;

    case EVENT_LWM2M_READY:
        {
            int queue_send = 255;
            tal_queue_post(ty_test_queue, &queue_send, 0);
        }
        break;

    case EVENT_POWERKEY_PRESS:
        break;

    case EVENT_DEVICE_BIND_ON:
        break;

    case EVENT_DEVICE_UNBIND_ON:
        packet_send_sum = 0;
        packet_send_succeed = 0;
        on_time = 0;
        tuya_sample_kv_write(SAMPLE_PACKET_SEND_SUM, 0);
        tuya_sample_kv_write(SAMPLE_PACKET_SEND_SUCCED, 0);
        tuya_user_api_config_item_delete(SAMPLE_PACKET_SEND_SUM);
        tuya_user_api_config_item_delete(SAMPLE_PACKET_SEND_SUCCED);
        break;

    default:
        break;
    }
    return OPRT_OK;
}

battery_state_t hal_battery_level_get(void)
{
    return BATTERY_LEVEL_NORMAL;
}

static void fota_event_notify(tuya_fota_event_id_t event_id)
{
    USER_API_LOGD("fota_event_notify:%d",event_id);
    iot_board_sensor_display(event_id + 100, NULL, 0);
    switch (event_id) { 
    case OTA_EVENT_DOWNLOAD_START:

        break;
    case OTA_EVENT_DOWNLOAD_COMPLETE:

        break;
    case OTA_EVENT_DONWLOAD_FAIL:

        break;

    case OTA_EVENT_UPDATE_START:

        break;

    case OTA_EVENT_UPDATE_SUCCESS:
     
        break;

    case OTA_EVENT_UPDATE_FAIL:

        break;

    case OTA_EVENT_SCHEMA_DONWLOAD_START:

        break;
    case OTA_EVENT_SCHEMA_DONWLOAD_COMPLETE:

        break;
    case OTA_EVENT_SCHEMA_DONWLOAD_FAIL:

        break;

    default:
        break;
    };
}

static void tuya_file_download_schedule(uint32_t download_percent )
{

    iot_board_sensor_display(IOT_FILE_DOWNLOAD_SCHEDULE, &download_percent, sizeof(uint32_t));
}

static void tuya_thread_test_task(void* pvParameters)
{
    int queue_rcv = 0;
    while(1)
    {
        //消息队列接收
        int ret = tal_queue_fetch(ty_test_queue, &queue_rcv, 0xFFFF); //最长等待1s;如果需要阻塞等，第三个参数使用:QUEUE_WAIT_FROEVER
        if(ret == OPRT_OK){
            USER_API_LOGD("tuya test queue received: %d", queue_rcv);
            dev_state_report();
        }

        USER_API_LOGD("tuya test thread is running!!!");
    }
}

int device_init(void)
{
    int ret = OPRT_OK;

    // Sleep lock
    //tuya_user_api_psm_disable();

    iot_board_sensor_get_vbat();

    iot_board_sensor_init();

    // Set PID first
    tuya_user_api_set_product_key(PRODUCT_KEY);

    tuya_user_api_write_t3324(2);

    // event callback
    tuya_user_api_event_loop_set_cb(tuya_event_process_cb, NULL);
    tuya_user_api_event_loop_start();

    // fota OTA
    tuya_fota_context_t app_fota_ctx = {
        .hal_battery_level_get      = hal_battery_level_get,
        .event_notify               = fota_event_notify,
        .tuya_file_download_precent = tuya_file_download_schedule
    };
    tuya_user_api_fota_notify_register(&app_fota_ctx);

    // tuya_dp 
    tuya_user_api_dp_write_default_cb(tuya_dp_write_cb);
    tuya_user_api_dp_report_record_ack_register_cb(dp_report_notify_callback);

    tuya_sample_kv_read(SAMPLE_PACKET_SEND_SUM, &packet_send_sum);
    tuya_sample_kv_read(SAMPLE_PACKET_SEND_SUCCED, &packet_send_succeed);
    tuya_user_api_get_system_runtime((TIME_S *)&on_time, NULL);
    tuya_user_api_lifetime_set(1200);
	tuya_user_api_record_dp_lifetime_set(600);

    if (tuya_user_api_is_dev_binded() || (TAL_FIRST_BOOT_UP == tuya_user_api_get_powerOn_result())){
        packet_send_sum++;
        tuya_sample_kv_write(SAMPLE_PACKET_SEND_SUM, packet_send_sum);
    }
    USER_API_LOGW("power on,rssi:%d, lifetime:%d,[sum:%d,success:%d], on time:%d(s), on rst:%d,", \
                tuya_user_api_rssi_get(), tuya_user_api_get_dev_lifetime(), packet_send_sum, packet_send_succeed,\
                on_time, tuya_user_api_get_powerOn_result());    

    //sample 1:message queue create and send
    tal_queue_create_init(&ty_test_queue, sizeof(int), 10);

    //sample 2:task create(note：the value of task more big,then the task priority is more higher.don't be more than 3)
    THREAD_CFG_T thread_cfg = {
        .priority = TASK_PRIO_NORMAL,  //task priority
        .stackDepth = 1024 * 2,             //task stack
        .thrdname = "ty_thread_test",       //task name,don't longer than 15
    };
    ret = tal_thread_create_and_start(&ty_test_thread, NULL, NULL, tuya_thread_test_task, NULL, &thread_cfg);

    return ret;
}
